<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    body {
      height: 100%;
      overflow: hidden;
    }

    #canvas {
      background-color: black;
    }
  </style>
</head>

<body>
  <canvas id="canvas"></canvas>
  <script>
    /** @type {HTMLCanvasElement} */
    const canvas = document.querySelector('#canvas')
    // 设置画布
    const ctx = canvas.getContext('2d')

    const width = canvas.width = innerWidth
    const height = canvas.height = innerHeight

    // 生成[max,min]的随机数
    function random(max, min) {
      const num = Math.floor(Math.random() * (max - min)) + min
      return num
    }
    // 随机颜色
    function randomColor() {
      return 'rgb(' + random(0, 255) + ',' +
        random(0, 255) + ',' +
        random(0, 255) + ')'
    }

    // 外形构造器
    function Shape(x, y, xSpeed, ySpeed, exists) {
      this.x = x
      this.y = y
      this.xSpeed = xSpeed
      this.ySpeed = ySpeed
      this.exists = exists
    }
    // 继承的验证方法
    Shape.prototype.fn = function () {
      console.log('我是Shape的方法，儿子继承');
    }

    // 球构造器
    function Ball(x, y, xSpeed, ySpeed, exists, color, size) {
      Shape.call(this, x, y, xSpeed, ySpeed, exists)
      this.color = color
      this.size = size
    }

    // 画出小球的方法
    Ball.prototype.draw = function () {
      // 声明我们现在要开始在纸上画一个图形了
      ctx.beginPath();
      ctx.fillStyle = this.color;
      ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI)
      ctx.fill()
    }

    // 更新小球的方法(加边缘碰撞检测)
    Ball.prototype.update = function () {
      if ((this.x - this.size) <= 0 || (this.x + this.size >= width)) {
        this.xSpeed = -this.xSpeed
      }
      if ((this.y - this.size) <= 0 || (this.y + this.size >= height)) {
        this.ySpeed = -this.ySpeed
      }
      this.x += this.xSpeed
      this.y += this.ySpeed
    }

    //小球碰撞检测  碰撞后换颜色
    Ball.prototype.edgeDetection = function () {
      // 遍历所有小球
      for (let i = 0; i < balls.length; i++) {
        // if语句判断调用方法的小球是否为当前遍历的小球 
        if (this !== balls[i]) {
          const dx = this.x - balls[i].x
          const dy = this.y - balls[i].y
          const distance = Math.sqrt(dx * dx + dy * dy)
          // 两个小球中心的距离小于两小球的半径和则说明两小球相撞
          if (distance < this.size + balls[i].size) {
            // 将两个小球的颜色都设置成随机的一种
            balls[i].color = randomColor()
            this.color = randomColor()
          }
        }
      }
    }

    // Ball继承Shape 这一步很重要，虽然实现了功能，但是没有实现方法的继承
    Ball.prototype = Object.create(Shape.prototype)
    // 或这样写 Ball.prototype = new Person()  不推荐
    Ball.prototype.constructor = Ball

    // 控制的球  构造器
    function ControlBall(x, y, exists) {
      Shape.call(this, x, y, 20, 20, exists)
      this.color = 'white'
      this.size = 10
    }

    // ControlBall 画球的方法
    ControlBall.prototype.draw = function () {
      ctx.beginPath();
      // 边框的颜色
      ctx.strokeStyle = this.color;
      ctx.lineWidth = 3;
      ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
      // 描边
      ctx.stroke();
    }

    // ControlBall 运动的方法
    ControlBall.prototype.sport = function () {
      //添加键盘事件
      window.onkeydown = e => {
        switch (e.key) {
          case 'ArrowLeft':
            this.x -= this.xSpeed;
            break
          case 'ArrowRight':
            this.x = this.x + this.xSpeed
            break
          case 'ArrowUp':
            this.y = this.y - this.ySpeed
            break
          case 'ArrowDown':
            this.y = this.y + this.ySpeed
            break
        }
      }
    }

    // 定义 ControlBall 的边缘检测方法
    ControlBall.prototype.checkBounds = function () {
      if ((this.x + this.size) >= width) {
        this.x = width - this.size
      }

      if ((this.x - this.size) <= 0) {
        this.x = this.size
      }

      if ((this.y + this.size) >= height) {
        this.y = height - this.size
      }

      if ((this.y - this.size) <= 0) {
        this.y = this.size
      }
    };

    // 定义 ControlBall 冲突检测函数(判断是否撞小球)
    ControlBall.prototype.collisionDetect = function () {
      for (let i = 0; i < balls.length; i++) {
        if (balls[i].exists) {
          const dx = this.x - balls[i].x
          const dy = this.y - balls[i].y
          const distance = Math.sqrt(dx * dx + dy * dy)
          console.log();

          if (distance <= this.size + balls[i].size) {
            balls[i].exists = false
            this.size += 2
          }
        }
      }
    }

    // Ball继承Shape 这一步很重要，虽然实现了功能，但是没有实现方法的继承
    ControlBall.prototype = Object.create(Shape.prototype)
    // 或这样写 ControlBall.prototype = new Person()  不推荐
    ControlBall.prototype.constructor = ControlBall

    let control = new ControlControlBall(random(0, width), random(0, height), true)
    control.sport()

    // 存储小球
    let balls = []
    // 生成小球
    while (balls.length < 50) {
      let size = random(10, 20)
      let ball = new Ball(
        // 球至少离画布边缘球本身一倍宽度的距离
        random(0 + size, width - size),
        random(0 + size, height - size),
        random(-7, 7),
        random(-7, 7),
        true,
        randomColor(),
        size,
      )
      balls.push(ball)
    }

    //运动
    function loop() {
      ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
      ctx.fillRect(0, 0, width, height)
      for (let i = 0; i < balls.length; i++) {
        // 产生碰撞的小球
        balls[i].exists ? "" : balls.splice(i, 1)
        balls[i].draw()
        balls[i].update()
        balls[i].edgeDetection()

      }
      control.draw()
      control.checkBounds()
      control.collisionDetect()
    }
    let ballLoop = setInterval(loop, 50)



  </script>
</body>

</html>